package service

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/jinzhu/copier"
	"github.com/robfig/cron/v3"
	"go.uber.org/zap"
	v1 "golang-sgg-daijia/api/v1"
	"golang-sgg-daijia/internal/common/util"
	"golang-sgg-daijia/internal/const/enum"
	"golang-sgg-daijia/internal/const/orderconst"
	"golang-sgg-daijia/internal/const/redisconst"
	"golang-sgg-daijia/internal/model"
	"golang-sgg-daijia/internal/repository/mysql"
	"golang-sgg-daijia/internal/repository/redis"
	"golang-sgg-daijia/internal/repository/tencentmap"
	"golang-sgg-daijia/internal/service/rules"
	"math"
	"strconv"
	"time"
)

type OrderService interface {
	CalculateDrivingLine(ctx context.Context, req *v1.CalculateDrivingLineForm) (*v1.DrivingLineVo, error)
	ExpectOrder(ctx context.Context, req *v1.CalculateDrivingLineForm) (*v1.ExpectOrderVo, error)
	SubmitOrder(ctx context.Context, req *v1.SubmitOrderForm) (int64, error)
	GetOrderStatus(ctx context.Context, orderID int) (int, error)
	FindNewOrderQueueData(ctx context.Context, driverID int64) ([]*v1.OrderData, error)
	ClearOrderQueueData(ctx context.Context, driverID int64) (bool, error)
	SearchDriverCurrentOrder(ctx context.Context, driverID int64) (*v1.CurrentOrderInfoVo, error)
	RobNewOrder(ctx context.Context, driverID int64, orderID int64) (bool, error)
	SearchCustomerCurrentOrder(ctx context.Context, customerID int64) (*v1.CurrentOrderInfoVo, error)
	GetDriverOrderInfo(ctx context.Context, orderID int, driverID int64) (*v1.OrderInfoVo, error)
	GetCustomerOrderInfo(ctx context.Context, orderID int, customerID int64) (*v1.OrderInfoVo, error)
	DriverArriveStartLocation(ctx context.Context, orderID int, driverID int64) (bool, error)
	UpdateOrderCart(ctx context.Context, req *v1.UpdateOrderCartForm) (bool, error)
	StartDrive(ctx context.Context, startDriveForm *v1.StartDriveForm) (bool, error)
	GetDriverInfo(ctx context.Context, orderID int, customerID int64) (*v1.DriverInfoVo, error)
	UpdateEndDriveData(ctx context.Context, updateOrderBillForm *v1.UpdateOrderBillForm) (bool, error)
	EndDrive(ctx context.Context, orderFeeForm *v1.OrderFeeForm) (bool, error)
	FindCustomerOrderPage(ctx context.Context, customerId int64, page int, limit int) (*v1.OrderListPageVo, error)
	FindDriverOrderPage(ctx context.Context, driverID int64, page int, limit int) (*v1.OrderListPageVo, error)
	SendOrderBillInfo(ctx context.Context, orderID int, driverID int64) (bool, error)
}

func NewOrderService(
	service *Service,
	mapcli tencentmap.MapClient,
	orderInfoDao mysql.OrderInfoDao,
	orderJobDao mysql.OrderJobDao,
	orderBillDao mysql.OrderBillDao,
	orderProfitsharingDao mysql.OrderProfitsharingDao,
	driverInfoDao mysql.DriverInfoDao,
	locationService LocationService,
	rdb redis.RedisClient,
) OrderService {
	return &orderService{
		Service:               service,
		mapcli:                mapcli,
		orderInfoDao:          orderInfoDao,
		orderJobDao:           orderJobDao,
		orderBillDao:          orderBillDao,
		orderProfitsharingDao: orderProfitsharingDao,
		driverInfoDao:         driverInfoDao,
		locationService:       locationService,
		rdb:                   rdb,
	}
}

type orderService struct {
	*Service
	locationService       LocationService
	mapcli                tencentmap.MapClient
	orderInfoDao          mysql.OrderInfoDao
	orderJobDao           mysql.OrderJobDao
	orderBillDao          mysql.OrderBillDao
	orderProfitsharingDao mysql.OrderProfitsharingDao
	driverInfoDao         mysql.DriverInfoDao
	rdb                   redis.RedisClient
}

func (s *orderService) SendOrderBillInfo(ctx context.Context, orderID int, driverID int64) (bool, error) {
	updateOrderInfo := &model.OrderInfo{
		Status: int32(enum.Unpaid.Value()),
	}
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, updateOrderInfo, map[string]interface{}{
		"id":        orderID,
		"driver_id": driverID,
	})
	if err != nil {
		return false, err
	}
	if n != 1 {
		return false, v1.ErrDataError
	}
	return true, nil
}

func (s *orderService) FindDriverOrderPage(ctx context.Context, driverID int64, page int, limit int) (*v1.OrderListPageVo, error) {
	orderInfoList, total, err := s.orderInfoDao.FindPageByCustomerID(ctx, driverID, page, limit)
	orderListVo := make([]*v1.OrderListVo, len(orderInfoList))
	for _, v := range orderInfoList {
		orderVo := &v1.OrderListVo{
			ID:            v.ID,
			OrderNo:       v.OrderNo,
			StartLocation: v.StartLocation,
			EndLocation:   v.EndLocation,
			Amount:        v.ExpectAmount,
			Status:        int(v.Status),
		}
		if v.Status > int32(enum.Unpaid.Value()) {
			orderVo.Amount = v.RealAmount
		}
		orderListVo = append(orderListVo, orderVo)
	}
	if err != nil {
		return nil, err
	}
	pages := int64(math.Ceil(float64(total) / float64(limit)))
	ret := v1.NewOrderListPageVo(orderListVo, pages, total)
	ret.Page = int64(page)
	ret.Limit = int64(limit)
	return ret, nil
}

func (s *orderService) FindCustomerOrderPage(ctx context.Context, customerId int64, page int, limit int) (*v1.OrderListPageVo, error) {
	orderInfoList, total, err := s.orderInfoDao.FindPageByCustomerID(ctx, customerId, page, limit)
	orderListVo := make([]*v1.OrderListVo, len(orderInfoList))
	for _, v := range orderInfoList {
		orderVo := &v1.OrderListVo{
			ID:            v.ID,
			OrderNo:       v.OrderNo,
			StartLocation: v.StartLocation,
			EndLocation:   v.EndLocation,
			Amount:        v.ExpectAmount,
			Status:        int(v.Status),
		}
		if v.Status > int32(enum.Unpaid.Value()) {
			// todo 确认是否支付，已支付从bill表中取支付金额，懒得改join查询了，每次去查采用性能差不少
			bill, err := s.orderBillDao.FindByOrderID(ctx, v.ID)
			if err != nil {
				return nil, err
			}
			orderVo.Amount = bill.PayAmount
		}
		orderListVo = append(orderListVo, orderVo)
	}
	if err != nil {
		return nil, err
	}
	pages := int64(math.Ceil(float64(total) / float64(limit)))
	ret := v1.NewOrderListPageVo(orderListVo, pages, total)
	ret.Page = int64(page)
	ret.Limit = int64(limit)
	return ret, nil
}

// EndDrive 结束代驾
func (s *orderService) EndDrive(ctx context.Context, orderFeeForm *v1.OrderFeeForm) (bool, error) {
	// 没有调用外部服务，就懒得弄异步编排了，go可以直接用waitGroup不用Future那样的库
	// 1 根据orderId获取订单信息，判断当前订单是否司机接单
	orderInfo, err := s.orderInfoDao.GetByID(ctx, int(orderFeeForm.OrderID))
	if err != nil {
		return false, err
	}
	if orderInfo.DriverID != orderFeeForm.DriverID {
		return false, v1.ErrIllegalRequest
	}

	// 防止刷单
	location, err := s.locationService.GetOrderServiceLastLocation(ctx, orderFeeForm.OrderID)
	if err != nil {
		return false, err
	}
	// 司机当前位置， 距离 结束代驾位置
	curDistance := util.GetDistance(orderInfo.EndPointLatitude, orderInfo.EndPointLongitude, location.Latitude, location.Longitude)
	if curDistance < orderconst.DriverEndLocationDistance {
		return false, v1.ErrEndLocationDistance
	}

	// 2 计算订单实际里程
	realDistance, err := s.locationService.CalculateOrderRealDistance(ctx, orderFeeForm.OrderID)
	if err != nil {
		return false, err
	}

	// 3 计算代驾实际费用
	// 实际费用= 代驾费用 + 其他费用（停车费等）
	waitMinute := (orderInfo.StartServiceTime.Unix() - orderInfo.ArriveTime.Unix()) / 60 / 60
	feeReq := &v1.FeeRuleRequestForm{
		Distance:   realDistance,
		StartTime:  orderInfo.StartServiceTime,
		WaitMinute: int(waitMinute), // 计算司机到达代驾开始位置时间
	}
	feeRuleRsp := rules.NewFeeRuleImplement().CalculateAllFee(feeReq)
	totalAmount := feeRuleRsp.TotalAmount + orderFeeForm.OtherFee +
		orderFeeForm.ParkingFee + orderFeeForm.TollFee + orderInfo.FavourFee
	feeRuleRsp.TotalAmount = totalAmount

	// 4 计算系统奖励
	curDate := orderInfo.StartServiceTime
	startTime := time.Date(curDate.Year(), curDate.Month(), curDate.Day(), 0, 0, 0, 0, curDate.Location())
	endTime := time.Date(curDate.Year(), curDate.Month(), curDate.Day(), 23, 59, 59, 0, curDate.Location())
	orderNum, err := s.orderInfoDao.GetOrderNumByTime(ctx, orderInfo.DriverID, startTime, endTime)
	rewardReq := &v1.RewardRuleRequestForm{
		StartTime: orderInfo.StartServiceTime,
		OrderNum:  orderNum,
	}
	rewardRsp := rules.NewRewardRuleImplement().CalculateAllReward(rewardReq)
	// 5 计算分账信息
	profitsharingReq := &v1.ProfitsharingRuleRequest{
		OrderAmount: totalAmount,
		OrderNum:    orderNum,
	}
	profitsharingRsp := rules.NewProfitSharingRuleImplement().CalculateProfitSharing(profitsharingReq)
	// 6 封装实体类，结束代驾更新订单，添加账单和分账信息
	updateOrderBillForm := &v1.UpdateOrderBillForm{}
	updateOrderBillForm.OrderID = orderFeeForm.OrderID
	updateOrderBillForm.DriverID = orderFeeForm.DriverID
	// 费用
	updateOrderBillForm.TollFee = orderFeeForm.TollFee
	updateOrderBillForm.OtherFee = orderFeeForm.OtherFee
	updateOrderBillForm.ParkingFee = orderFeeForm.ParkingFee
	updateOrderBillForm.FavourFee = orderInfo.FavourFee

	updateOrderBillForm.RealDistance = realDistance
	// 订单奖励信息
	err = copier.Copy(updateOrderBillForm, rewardRsp)
	// 代驾费用信息
	err = copier.Copy(updateOrderBillForm, feeRuleRsp)
	// 分账相关信息
	err = copier.Copy(updateOrderBillForm, profitsharingRsp)
	updateOrderBillForm.ProfitsharingRuleID = profitsharingRsp.ProfitsharingRuleId

	return s.UpdateEndDriveData(ctx, updateOrderBillForm)
}

// UpdateEndDriveData 结束代驾更新订单，添加账单和分账信息
func (s *orderService) UpdateEndDriveData(ctx context.Context, updateOrderBillForm *v1.UpdateOrderBillForm) (bool, error) {
	// 1 更新订单信息
	orderInfo := &model.OrderInfo{
		Status:         int32(enum.EndService.Value()),
		RealAmount:     updateOrderBillForm.TotalAmount,
		FavourFee:      updateOrderBillForm.FavourFee,
		RealDistance:   updateOrderBillForm.RealDistance,
		EndServiceTime: time.Now(),
	}
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, orderInfo, map[string]interface{}{
		"id":        updateOrderBillForm.OrderID,
		"driver_id": updateOrderBillForm.DriverID,
	})
	if err != nil {
		return false, err
	}
	if n == 1 {
		// 添加账单数据
		orderBill := &model.OrderBill{}
		err = copier.Copy(orderBill, updateOrderBillForm)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|EndDrive|Copy", zap.Any("data", map[string]interface{}{
				"err": err,
			}), zap.Error(err))
		}
		orderBill.OrderID = updateOrderBillForm.OrderID
		orderBill.PayAmount = updateOrderBillForm.TotalAmount
		err = s.orderBillDao.Create(ctx, orderBill)
		if err != nil {
			return false, err
		}

		// 添加分账信息
		orderProfitsharing := &model.OrderProfitsharing{}
		err = copier.Copy(orderProfitsharing, updateOrderBillForm)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|EndDrive|Copy", zap.Any("data", map[string]interface{}{
				"err": err,
			}), zap.Error(err))
		}
		orderProfitsharing.OrderID = updateOrderBillForm.OrderID
		orderProfitsharing.RuleID = updateOrderBillForm.ProfitsharingRuleID
		orderProfitsharing.Status = int32(enum.SharingSuccess.Value())
		err = s.orderProfitsharingDao.Create(ctx, orderProfitsharing)
		if err != nil {
			return false, err
		}
	} else {
		return false, v1.ErrUpdateOrderFail
	}
	return true, nil
}

func (s *orderService) GetDriverOrderInfo(ctx context.Context, orderID int, driverID int64) (*v1.OrderInfoVo, error) {
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderID)
	if err != nil {
		return nil, err
	}
	if orderInfo == nil {
		return nil, nil
	}
	// 这接口用于历史订单查询，此处用于防止篡改请求，获取到了其他用户的数据
	if orderInfo.DriverID != driverID {
		return nil, v1.ErrIllegalRequest
	}
	orderInfoVo := &v1.OrderInfoVo{
		OrderID:             orderInfo.ID,
		OrderNo:             orderInfo.OrderNo,
		StartLocation:       orderInfo.StartLocation,
		StartPointLongitude: orderInfo.StartPointLongitude,
		StartPointLatitude:  orderInfo.StartPointLatitude,
		EndLocation:         orderInfo.EndLocation,
		EndPointLongitude:   orderInfo.EndPointLongitude,
		EndPointLatitude:    orderInfo.EndPointLatitude,
		Status:              int(orderInfo.Status),
		CreateTime:          orderInfo.CreateTime,
	}
	orderBillVo := &v1.OrderBillVo{}
	orderProfitVo := &v1.OrderProfitsharingVo{}

	if int(orderInfo.Status) >= enum.EndService.Value() {
		orderBill, err := s.orderBillDao.FindByOrderID(ctx, int64(orderID))
		if err != nil {
			return nil, err
		}
		_ = copier.Copy(orderBillVo, orderBill)

		orderProfit, err := s.orderProfitsharingDao.FindByOrderID(ctx, int64(orderID))
		if err != nil {
			return nil, err
		}

		_ = copier.Copy(orderProfitVo, orderProfit)
	}
	orderInfoVo.OrderBillVo = orderBillVo
	orderInfoVo.OrderProfitsharingVo = orderProfitVo

	return orderInfoVo, nil
}

func (s *orderService) GetCustomerOrderInfo(ctx context.Context, orderID int, customerID int64) (*v1.OrderInfoVo, error) {
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderID)
	if err != nil {
		return nil, err
	}
	if orderInfo == nil {
		return nil, nil
	}
	// 这接口用于历史订单查询，此处用于防止篡改请求，获取到了其他用户的数据
	if orderInfo.CustomerID != customerID {
		return nil, v1.ErrIllegalRequest
	}
	orderInfoVo := &v1.OrderInfoVo{
		OrderID:             orderInfo.ID,
		OrderNo:             orderInfo.OrderNo,
		StartLocation:       orderInfo.StartLocation,
		StartPointLongitude: orderInfo.StartPointLongitude,
		StartPointLatitude:  orderInfo.StartPointLatitude,
		EndLocation:         orderInfo.EndLocation,
		EndPointLongitude:   orderInfo.EndPointLongitude,
		EndPointLatitude:    orderInfo.EndPointLatitude,
		Status:              int(orderInfo.Status),
		CreateTime:          orderInfo.CreateTime,
	}
	driverInfoVo := &v1.DriverInfoVo{}
	var driverInfo *model.DriverInfo
	if orderInfo.DriverID != 0 {
		driverInfo, err = s.driverInfoDao.GetByID(ctx, orderInfo.DriverID)
	}
	_ = copier.Copy(driverInfoVo, driverInfo)
	orderInfoVo.DriverInfoVo = driverInfoVo

	orderBillVo := &v1.OrderBillVo{}

	if int(orderInfo.Status) >= enum.Unpaid.Value() {
		orderBill, err := s.orderBillDao.FindByOrderID(ctx, int64(orderID))
		if err != nil {
			return nil, err
		}
		_ = copier.Copy(orderBillVo, orderBill)
	}
	orderInfoVo.OrderBillVo = orderBillVo
	return orderInfoVo, nil
}

func (s *orderService) SearchDriverCurrentOrder(ctx context.Context, driverID int64) (*v1.CurrentOrderInfoVo, error) {
	conditions := map[string]interface{}{
		"driver_id": driverID,
	}
	status := []int{
		enum.Accepted.Value(),
		enum.DriverArrived.Value(),
		enum.UpdateCartInfo.Value(),
		enum.StartService.Value(),
		enum.EndService.Value(),
	}
	orderInfo, err := s.orderInfoDao.FindCurrentOrder(ctx, conditions, status)
	if err != nil {
		return nil, err
	}
	currentOrderInfoVo := &v1.CurrentOrderInfoVo{}
	if orderInfo != nil {
		currentOrderInfoVo.Status = int(orderInfo.Status)
		currentOrderInfoVo.OrderID = orderInfo.ID
		currentOrderInfoVo.IsHasCurrentOrder = true
	} else {
		currentOrderInfoVo.IsHasCurrentOrder = false
	}
	return currentOrderInfoVo, nil
}

func (s *orderService) SearchCustomerCurrentOrder(ctx context.Context, customerID int64) (*v1.CurrentOrderInfoVo, error) {
	conditions := map[string]interface{}{
		"customer_id": customerID,
	}
	status := []int{
		enum.Accepted.Value(),
		enum.DriverArrived.Value(),
		enum.UpdateCartInfo.Value(),
		enum.StartService.Value(),
		enum.EndService.Value(),
	}
	orderInfo, err := s.orderInfoDao.FindCurrentOrder(ctx, conditions, status)
	if err != nil {
		return nil, err
	}
	currentOrderInfoVo := &v1.CurrentOrderInfoVo{}
	if orderInfo != nil {
		currentOrderInfoVo.Status = int(orderInfo.Status)
		currentOrderInfoVo.OrderID = orderInfo.ID
		currentOrderInfoVo.IsHasCurrentOrder = true
	} else {
		currentOrderInfoVo.IsHasCurrentOrder = false
	}
	return currentOrderInfoVo, nil
}

func (s *orderService) RobNewOrder(ctx context.Context, driverID int64, orderID int64) (bool, error) {
	// 判断订单是否存在，通过redis减少数据库压力
	flag, err := s.rdb.Exists(ctx, redisconst.OrderAcceptMark+strconv.FormatInt(orderID, 10))
	if err != nil {
		return false, err
	}
	if !flag {
		return true, nil
	}
	// 司机抢单
	// 修改order_info表订单状态值为 已经接单 + 司机ID + 司机接单时间
	// 修改条件 订单id
	orderInfo, err := s.orderInfoDao.GetByID(ctx, int(orderID))
	if err != nil {
		return false, err
	}
	orderInfo.DriverID = driverID
	orderInfo.AcceptTime = time.Now()
	orderInfo.Status = int32(enum.Accepted.Value())
	//n, err := o.orderInfoDao.UpdateByID(ctx, orderInfo)
	// status 为乐观锁
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, orderInfo, map[string]interface{}{
		"id":     orderID,
		"status": enum.WaitingAccept.Value(),
	})
	if err != nil {
		return false, err
	}
	if n != 1 {
		return false, nil
	}
	// 删除抢单表示
	_, err = s.rdb.Del(ctx, redisconst.OrderAcceptMark+strconv.FormatInt(orderID, 10))

	return true, nil
}

func (s *orderService) FindNewOrderQueueData(ctx context.Context, driverID int64) ([]*v1.OrderData, error) {
	key := redisconst.DriverOrderTempList + strconv.FormatInt(driverID, 10)
	size, err := s.rdb.LLen(ctx, key)
	if err != nil {
		return nil, err
	}
	var orderDataList []*v1.OrderData
	if size > 0 {
		for i := 0; i < int(size); i++ {
			val, err := s.rdb.LPop(ctx, key)
			if err != nil {
				continue
			}
			orderData := &v1.OrderData{}
			err = json.Unmarshal([]byte(val), orderData)
			if err != nil {
				s.logger.WithContext(ctx).Error("orderService|Login|GenToken", zap.Any("data", map[string]interface{}{
					"orderData": val,
				}), zap.Error(err))
				continue
			}
			orderDataList = append(orderDataList, orderData)
		}
	}
	return orderDataList, nil
}

func (s *orderService) GetOrderStatus(ctx context.Context, orderID int) (int, error) {
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderID)
	if err != nil {
		return 0, err
	}
	if orderInfo == nil {
		return enum.NullOrder.Value(), nil
	}
	return int(orderInfo.Status), nil
}

func (s *orderService) ExpectOrder(ctx context.Context, req *v1.CalculateDrivingLineForm) (*v1.ExpectOrderVo, error) {
	drivingLine, err := s.mapcli.RoutesDriving(ctx, req)
	if err != nil {
		return nil, err
	}
	feeRuleReq := &v1.FeeRuleRequestForm{
		Distance:   drivingLine.Distance,
		StartTime:  time.Now(),
		WaitMinute: 0,
	}
	feeRuleResp := rules.NewFeeRuleImplement().CalculateAllFee(feeRuleReq)
	return &v1.ExpectOrderVo{
		DrivingLineVo:   *drivingLine,
		FeeRuleResponse: *feeRuleResp,
	}, nil
}

func (s *orderService) CalculateDrivingLine(ctx context.Context, req *v1.CalculateDrivingLineForm) (*v1.DrivingLineVo, error) {
	return s.mapcli.RoutesDriving(ctx, req)
}

// SubmitOrder 乘客下单
func (s *orderService) SubmitOrder(ctx context.Context, req *v1.SubmitOrderForm) (int64, error) {
	// 1 重新计算驾驶路线
	drivingLineForm := &v1.CalculateDrivingLineForm{
		EndPointLatitude:    req.EndPointLatitude,
		EndPointLongitude:   req.EndPointLongitude,
		StartPointLatitude:  req.StartPointLatitude,
		StartPointLongitude: req.StartPointLongitude,
	}
	drivingLine, err := s.mapcli.RoutesDriving(ctx, drivingLineForm)
	if err != nil {
		return 0, err
	}

	// 2 重新订单费用
	feeRuleReq := &v1.FeeRuleRequestForm{
		Distance:   drivingLine.Distance,
		StartTime:  time.Now(),
		WaitMinute: 0,
	}
	feeRuleResp := rules.NewFeeRuleImplement().CalculateAllFee(feeRuleReq)

	// 封装数据
	orderInfoForm := &v1.OrderInfoForm{}
	err = copier.Copy(orderInfoForm, req)
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|SubmitOrder|Copy", zap.Error(err))
		return 0, err
	}
	orderInfoForm.ExpectDistance = drivingLine.Distance
	orderInfoForm.ExpectAmount = feeRuleResp.TotalAmount
	// 保存数据
	orderInfo := &model.OrderInfo{}
	err = copier.Copy(orderInfo, orderInfoForm)
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|SubmitOrder|Copy", zap.Error(err))
		return 0, err
	}
	orderNo, err := s.sid.GenUint64()
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|SubmitOrder|GenUint64", zap.Error(err))
		return 0, err
	}
	orderInfo.Status = int32(enum.WaitingAccept.Value())
	orderInfo.OrderNo = strconv.FormatUint(orderNo, 10)
	err = s.orderInfoDao.Create(ctx, orderInfo)
	if err != nil {
		return 0, err
	}
	// 生成订单之后，发送延迟消息 todo
	//s.sendDelayMessage(orderInfo.ID)

	// 向redis中增加表示，用于司机抢单时不需要访问数据库
	err = s.rdb.Set(ctx, redisconst.OrderAcceptMark+strconv.FormatInt(orderInfo.ID, 10), "0",
		redisconst.OrderAcceptMarkExpiresTime)

	_, err = s.OrderTaskHandler(ctx, &v1.OrderTask{
		OrderID:             orderInfo.ID,
		StartLocation:       orderInfoForm.StartLocation,
		StartPointLongitude: orderInfoForm.StartPointLongitude,
		StartPointLatitude:  orderInfoForm.StartPointLatitude,
		EndLocation:         orderInfoForm.EndLocation,
		EndPointLongitude:   orderInfoForm.EndPointLongitude,
		EndPointLatitude:    orderInfoForm.EndPointLatitude,
		ExpectAmount:        orderInfoForm.ExpectAmount,
		ExpectDistance:      orderInfoForm.ExpectDistance,
		ExpectTime:          drivingLine.Duration,
		FavourFee:           orderInfoForm.FavourFee,
		CreateTime:          time.Now(),
	})
	if err != nil {
		return 0, err
	}

	return orderInfo.ID, nil
}

// OrderTaskHandler 乘客订单分发任务
func (s *orderService) OrderTaskHandler(ctx context.Context, orderTask *v1.OrderTask) (int64, error) {
	//1 判断当前订单是否启动任务调度
	//根据订单id查询
	orderJob, err := s.orderJobDao.FindByOrderID(ctx, orderTask.OrderID)
	if err != nil {
		return 0, err
	}
	//2 没有启动，进行操作
	if orderJob == nil {
		jobId, err := s.ExecuteOrderTask(ctx, orderTask)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|ExecuteOrderTask", zap.Error(err))
			return 0, err
		}
		// 记录任务调度信息
		param, err := json.Marshal(orderTask)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|Marshal", zap.Error(err))
			return 0, err
		}
		orderJob = &model.OrderJob{
			OrderID:   orderTask.OrderID,
			JobID:     jobId,
			Parameter: string(param),
		}
		err = s.orderJobDao.Create(ctx, orderJob)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|Create", zap.Error(err))
			return 0, err
		}
	}
	return orderJob.JobID, nil
}

func (s *orderService) ExecuteOrderTask(ctx context.Context, orderTask *v1.OrderTask) (int64, error) {
	c := cron.New(cron.WithSeconds())
	var jobId int64
	var err error
	OrderTaskFunc := func() {
		//1 根据jobid查询数据库，当前任务是否已经创建
		//如果没有创建，不往下执行了
		orderJob, err := s.orderJobDao.FindByJobID(ctx, jobId)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|ExecuteOrderTask|FindByJobID", zap.Error(err))
			return
		}
		if orderJob == nil {
			//不往下执行了
			return
		}
		//2 查询订单状态，如果当前订单接单状态，继续执行。如果当前订单不是接单状态，停止任务调度
		status, err := s.GetOrderStatus(ctx, int(orderTask.OrderID))
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|GetByID", zap.Error(err))
			return
		}
		if status != enum.WaitingAccept.Value() {
			//停止任务调度
			c.Stop()
			return
		}
		//3 搜索附近满足条件可以接单司机
		//4 获取满足可以接单司机集合
		searchNearByDriverForm := v1.SearchNearByDriverForm{
			Longitude:       orderTask.StartPointLongitude,
			Latitude:        orderTask.StartPointLatitude,
			MileageDistance: orderTask.ExpectDistance,
		}
		nearByDriverList, err := s.locationService.SearchNearByDriver(ctx, &searchNearByDriverForm)
		if err != nil {
			s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|SearchNearByDriver", zap.Error(err))
			return
		}
		//5 遍历司机集合，得到每个司机，为每个司机创建临时队列，存储新订单信息
		for _, driver := range nearByDriverList {
			//使用Redis的set类型
			//根据订单id生成key
			repeatKey := redisconst.DriverOrderRepeatList + strconv.FormatInt(orderTask.OrderID, 10)
			//记录司机id，防止重复推送
			isMember, err := s.rdb.SIsMember(ctx, repeatKey, driver.DriverID)
			if isMember || err != nil {
				continue
			}
			// 把订单信息推送给满足条件多个司机
			_, err = s.rdb.SAdd(ctx, repeatKey, driver.DriverID)
			if err != nil {
				continue
			}
			_, err = s.rdb.Expire(ctx, repeatKey, redisconst.DriverOrderRepeatListExpiresTime)
			if err != nil {
				continue
			}
			orderData := &v1.OrderData{
				OrderID:        orderTask.OrderID,
				StartLocation:  orderTask.StartLocation,
				EndLocation:    orderTask.EndLocation,
				ExpectAmount:   orderTask.ExpectAmount,
				ExpectDistance: orderTask.ExpectDistance,
				ExpectTime:     orderTask.ExpectTime,
				FavourFee:      orderTask.FavourFee,
				Distance:       driver.Distance,
				CreateTime:     orderTask.CreateTime,
			}
			//新订单保存司机的临时队列，Redis里面List集合
			key := redisconst.DriverOrderTempList + strconv.FormatInt(driver.DriverID, 10)
			orderDataStr, err := json.Marshal(orderData)
			if err != nil {
				s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|Marshal", zap.Error(err))
				continue
			}
			// 虽然是interface，但是自定义类型似乎不可以，需要转为string
			_, err = s.rdb.LPush(ctx, key, string(orderDataStr))
			if err != nil {
				continue
			}
			//过期时间：1分钟
			_, err = s.rdb.Expire(ctx, key, redisconst.DriverOrderTempListExpiresTime)
			if err != nil {
				continue
			}
		}
	}
	sec := time.Now().Second() + 5 // 5秒后，就开始每分钟执行一次
	if sec > 60 {
		sec = 0
	}
	spec := fmt.Sprintf("%d * * * * *", sec)
	_, err = c.AddFunc(spec, OrderTaskFunc) // 每分钟执行
	genUint64, err := s.sid.GenUint64()
	jobId = int64(genUint64)
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|OrderTaskHandler|AddFunc", zap.Error(err))
		return 0, err
	}
	c.Start()
	return jobId, nil
}

func (s *orderService) ClearOrderQueueData(ctx context.Context, driverID int64) (bool, error) {
	key := redisconst.DriverOrderTempList + strconv.FormatInt(driverID, 10)
	_, err := s.rdb.Del(ctx, key)
	if err != nil {
		return false, err
	}
	return true, nil
}

func (s *orderService) DriverArriveStartLocation(ctx context.Context, orderID int, driverID int64) (bool, error) {
	// 判断是否刷单
	// 代驾开始诶之
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderID)
	if err != nil {
		return false, err
	}
	// 司机当前位置
	location, err := s.locationService.GetCacheOrderLocation(ctx, int64(orderID))
	if err != nil {
		return false, err
	}
	// 位置距离，如果距离小于1公里，为刷单
	distance := util.GetDistance(orderInfo.StartPointLatitude, orderInfo.StartPointLongitude, location.Latitude, location.Longitude)
	if distance > orderconst.DriverStartLocationDistance {
		return false, v1.ErrStartLocationDistance
	}

	orderInfo = &model.OrderInfo{
		Status:     int32(enum.DriverArrived.Value()),
		ArriveTime: time.Now(),
	}
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, orderInfo, map[string]interface{}{
		"id":        orderID,
		"driver_id": driverID,
	})
	if err != nil {
		return false, err
	}
	if n != 1 {
		s.logger.WithContext(ctx).Error("orderService|UpdateByEqCondition|更新失败司机修改订单信息失败")
		return false, nil
	}
	return true, nil
}

func (s *orderService) UpdateOrderCart(ctx context.Context, req *v1.UpdateOrderCartForm) (bool, error) {
	orderInfo := &model.OrderInfo{}
	err := copier.Copy(orderInfo, req)
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|UpdateOrderCart|Copy", zap.Error(err))
		return false, err
	}
	orderInfo.Status = int32(enum.UpdateCartInfo.Value())
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, orderInfo, map[string]interface{}{
		"id":        req.OrderID,
		"driver_id": req.DriverID,
	})
	if err != nil {
		return false, err
	}
	if n != 1 {
		s.logger.WithContext(ctx).Info("orderService|UpdateOrderCart更新失败司机修改订单信息失败")
		return false, nil
	}
	return true, nil
}

// StartDrive 开始代价服务
func (s *orderService) StartDrive(ctx context.Context, startDriveForm *v1.StartDriveForm) (bool, error) {
	// 更具订单ID + 司机id 更新订单状态和代价时间
	orderInfo := &model.OrderInfo{
		Status:           int32(enum.StartService.Value()),
		StartServiceTime: time.Now(),
	}
	n, err := s.orderInfoDao.UpdateByEqCondition(ctx, orderInfo, map[string]interface{}{
		"id":        startDriveForm.OrderID,
		"driver_id": startDriveForm.DriverID,
	})
	if err != nil {
		return false, err
	}
	if n != 1 {
		s.logger.WithContext(ctx).Info("orderService|StartDrive更新失败司机修改订单信息失败")
		return false, nil
	}
	return true, nil
}

// GetDriverInfo 根据订单id获取司机基本信息
func (s *orderService) GetDriverInfo(ctx context.Context, orderID int, customerID int64) (*v1.DriverInfoVo, error) {
	//根据订单id获取订单信息
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderID)
	if err != nil {
		return nil, err
	}
	if orderInfo == nil {
		return nil, nil
	}
	if orderInfo.DriverID != orderInfo.DriverID {
		return nil, v1.ErrDataError
	}

	driverInfo, err := s.driverInfoDao.GetByID(ctx, orderInfo.DriverID)
	if err != nil {
		return nil, err
	}
	driverInfoVo := &v1.DriverInfoVo{}
	err = copier.Copy(driverInfoVo, driverInfo)
	if err != nil {
		s.logger.WithContext(ctx).Error("orderService|GetDriverInfo|Copy", zap.Error(err))
		return nil, err
	}

	//计算驾龄
	currentYear := time.Now().Year()
	//获取驾驶证初次领证日期
	//driver_license_issue_date
	firstYear := driverInfo.DriverLicenseIssueDate.Year()
	driverInfoVo.DriverLicenseAge = currentYear - firstYear

	return driverInfoVo, nil
}

func (s *orderService) listener() {
	go func(){
		defer func() {
			if err := recover(); err != nil {
				s.logger.Error("orderService|listener", zap.Error(err.(error)))
			}
		}()
		for {
			// 获取延迟队列

			// 获取消息
			//orderIdStr := s.getDelayMsg()
			var orderId int
			ctx := context.Background()
			flag, err := s.orderCancel(ctx, orderId)
			if err != nil {
				s.logger.WithContext(ctx).Error("orderService|orderCancel", zap.Error(err))
				continue
			}
			if !flag {
				s.logger.WithContext(ctx).Error("orderService|orderCancel|取消订单失败")
			}


		}
	}()
}

func (s *orderService) orderCancel(ctx context.Context, orderId int) (bool, error) {
	orderInfo, err := s.orderInfoDao.GetByID(ctx, orderId)
	if err != nil {
		return false, err
	}
	if orderInfo == nil {
		return false, nil
	}
	if orderInfo.Status == int32(enum.WaitingAccept.Value()) {
		orderInfo.Status = int32(enum.CancelOrder.Value())
		n, err := s.orderInfoDao.UpdateByID(ctx, orderInfo)
		if err != nil {
			return false, err
		}
		if n == 0 {
			return false, nil
		}
		// 删除接单标识
		n, err = s.rdb.Del(ctx, redisconst.OrderAcceptMark+strconv.FormatInt(orderInfo.ID, 10))
		if err != nil {
			return false, err
		}
		if n == 0 {
			return false, nil
		}
	}
	return true, nil
}